package zqq.servlet;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import zqq.annotations.EnjoyAuthowired;
import zqq.annotations.EnjoyController;
import zqq.annotations.EnjoyRequestMapping;
import zqq.annotations.EnjoyRequestParam;
import zqq.annotations.EnjoyService;
import zqq.controller.ZqqController;

/**
 * Servlet implementation class DispatcherServlet
 */
public class DispatcherServlet extends HttpServlet
{
	private static final long serialVersionUID = 1L;

	// 扫描得到的类名集合
	List<String> classNames = new ArrayList<String>();

	// 存放所有Spring实例的Map
	Map<String, Object> beans = new HashMap<String, Object>();

	// 存放所有路径映射
	Map<String, Object> handlerMap = new HashMap<String, Object>();

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public DispatcherServlet()
	{
	}

	/**
	 * @see Servlet#init(ServletConfig)
	 */
	public void init(ServletConfig config) throws ServletException
	{
		// 1、扫描工程有多少class
		doScanPackage("zqq");
		// 打印所有class
		for (String name : classNames)
		{
			System.out.println(name);
		}
		// 2、实例化
		doInstance();
		for (Map.Entry<String, Object> entry : beans.entrySet())
		{
			System.out.println(entry.getKey() + ":" + entry.getValue());
		}
		// 3、注入
		doIoC();

		// 4、请求映射
		buildMapping();

		for (Map.Entry<String, Object> entry : handlerMap.entrySet())
		{
			System.out.println(entry.getKey() + ":" + entry.getValue());
		}
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		doPost(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		// springmvc /zqq/query
		String uri = request.getRequestURI();

		String context = request.getContextPath(); // springmvc

		String path = uri.replace(context, ""); // /zqq/query

		// 获取映射对应的method
		Method method = (Method) handlerMap.get(path);

		ZqqController instance = (ZqqController) beans.get("/" + path.split("/")[1]);
		
		Object args[] = this.hand(request, response, method);
		
		try
		{
			method.invoke(instance, args);
		} catch (IllegalAccessException e)
		{
			e.printStackTrace();
		} catch (IllegalArgumentException e)
		{
			e.printStackTrace();
		} catch (InvocationTargetException e)
		{
			e.printStackTrace();
		}
		
	}

	/**
	 * @author qqz
	 * @date 2018年7月12日 上午1:02:44 扫描当前路径下有多少个class类
	 * @param string
	 */
	private void doScanPackage(String basePackage)
	{
//		URL url = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
		String filepath = this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/")).getFile();
		try
		{
			filepath= java.net.URLDecoder.decode(filepath,"utf-8");
		} catch (UnsupportedEncodingException e)
		{
			e.printStackTrace();
		}  
//		String fileStr = url.getFile();
		
		// 目录对象
		File file = new File(filepath);
		String[] filesStr = file.list();

		// 递归处理路径basepackage下的类文件
		for (String path : filesStr)
		{
			File filePath = new File(filepath + path);
			if (filePath.isDirectory())
			{
				doScanPackage(basePackage + "." + path);
			} else
			{
				// 得到class 全类名路径 zqq.controller.ZqqController
				classNames.add(basePackage + "." + filePath.getName());
			}
		}
	}

	/**
	 * @author qqz
	 * @date 2018年7月12日 上午1:11:04 TODO
	 */
	private void doInstance()
	{
		if (classNames.size() <= 0)
		{
			System.out.println("scan classes failed!");
		}

		for (String className : classNames)
		{
			// 去掉.class后缀
			String cn = className.replace(".class", "");

			try
			{
				Class<?> clazz = Class.forName(cn);
				// 处理带有EnjoyController注解的类
				if (clazz.isAnnotationPresent(EnjoyController.class))
				{
					// 实例化对象
					Object instance = clazz.newInstance();
					EnjoyRequestMapping reqMapping = clazz.getAnnotation(EnjoyRequestMapping.class);
					String key = reqMapping.value();
					beans.put(key, instance);
				} else if (clazz.isAnnotationPresent(EnjoyService.class))
				{
					// 实例化对象
					Object instance = clazz.newInstance();
					EnjoyService service = clazz.getAnnotation(EnjoyService.class);
					String key = service.value();
					beans.put(key, instance);
				} else
				{
					continue;
				}
			} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e)
			{
				e.printStackTrace();
			}
		}
	}

	/**
	 * @author qqz 属性注入
	 * @date 2018年7月12日 上午1:21:10 TODO
	 */
	private void doIoC()
	{
		if (beans.entrySet().size() <= 0)
		{
			System.out.println("instance bean failed.");
			return;
		}

		for (Map.Entry<String, Object> entry : beans.entrySet())
		{
			Object instance = entry.getValue();
			Class<?> clazz = instance.getClass();

			if (clazz.isAnnotationPresent(EnjoyController.class))
			{
				// 获取类中所有属性
				Field[] fields = clazz.getDeclaredFields();
				for (Field field : fields)
				{
					// 获取声明注入的属性
					if (field.isAnnotationPresent(EnjoyAuthowired.class))
					{
						EnjoyAuthowired authowired = field.getAnnotation(EnjoyAuthowired.class);
						// 获取注解EnjoyAutowired中命名的值
						String value = authowired.value();
						// 放开权限设置属性值
						field.setAccessible(true);
						try
						{
							field.set(instance, beans.get(value));
						} catch (IllegalArgumentException e)
						{
							e.printStackTrace();
						} catch (IllegalAccessException e)
						{
							e.printStackTrace();
						}
					} else
					{
						continue;
					}
				}
			}

		}
	}

	/**
	 * @author qqz
	 * @date 2018年7月12日 上午1:32:25 TODO
	 */
	private void buildMapping()
	{
		if (beans.entrySet().size() <= 0)
		{
			System.out.println("instance bean failed.");
			return;
		}

		for (Map.Entry<String, Object> entry : beans.entrySet())
		{
			Object instance = entry.getValue();
			Class<?> clazz = instance.getClass();
			// 映射是在Controller层
			if (clazz.isAnnotationPresent(EnjoyController.class))
			{
				// 获取类映射
				EnjoyRequestMapping requestMapping = clazz.getAnnotation(EnjoyRequestMapping.class);
				String classPath = requestMapping.value();

				// 获取方法上的映射
				Method[] methods = clazz.getMethods();

				for (Method method : methods)
				{
					if (method.isAnnotationPresent(EnjoyRequestMapping.class))
					{
						EnjoyRequestMapping requestMapping1 = method.getAnnotation(EnjoyRequestMapping.class);

						String methodPath = requestMapping1.value();

						// 构建方法路径与方法的映射
						handlerMap.put(classPath + methodPath, method);

					} else
					{
						continue;
					}
				}

			}
		}
	}

	/**
	 * @author qqz
	 * @date 2018年7月12日 上午1:59:48
	 * 方法参数注解解析
	 * @param request
	 * @param response
	 * @param method
	 * @return
	 */
	private static Object[] hand(HttpServletRequest request, HttpServletResponse response, Method method)
	{
		// 拿到当前执行的方法有哪些参数
		Class<?>[] paramClazzs = method.getParameterTypes();

		// 根据参数的个数，new 一个参数的数组， 将方法里所有参数赋值到args来
		Object[] args = new Object[paramClazzs.length];

		int arg_i = 0;
		int index = 0;
		for (Class<?> paramClazz : paramClazzs)
		{
			if (ServletRequest.class.isAssignableFrom(paramClazz))
			{
				args[arg_i++] = request;
			}

			if (ServletResponse.class.isAssignableFrom(paramClazz))
			{
				args[arg_i++] = response;
			}

			// 从0-3判断有没有RequestParam注解，很明显paramClazz为0和1时，不是，当为2和3时为@RequestParam，需要
			// 解析[@zqq.annotation.EnjoyRequestParam(value=name)]
			Annotation[] paramAns = method.getParameterAnnotations()[index];
			if (paramAns.length > 0)
			{
				for (Annotation paramAn : paramAns)
				{
					if (EnjoyRequestParam.class.isAssignableFrom(paramAn.getClass()))
					{
						EnjoyRequestParam rp = (EnjoyRequestParam)paramAn;
						//找到注解里的name和age
						args[arg_i++] = request.getParameter(rp.value());
					}
				}
			}
			index ++;
		}
		return args;
	}
}
